﻿using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Sebarf.Communication;
using Sebarf.Core;
using Sebarf.Core.Interfaces;
using Sebarf.Diagnostics.Interfaces;

namespace Sebarf.Server {
	/// <summary>
	/// Server class to retrieve information via WCF.
	/// </summary>
	/// <seealso cref="ServerQueryHandler"/>
	/// <seealso cref="IQueryRemoteHandler"/>
	[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single,
		IncludeExceptionDetailInFaults = true)]
	public class WcfBasedServer : IServer {
		#region IServer Members

		/// <summary>
		/// processes a request
		/// </summary>
		/// <param name="callerId"></param>
		/// <param name="request"></param>
		/// <returns></returns>
		public object Process(string callerId, IRequest request) {
			//on engine per client, if not existing, create it by the supplied create engine method
			if (!EnginesByCaller.ContainsKey(callerId)) {
				Logger.WriteInformation("new client connected:" + callerId);
				IRequestHandlerEngine engine = RequestHandlerEngineCreationMethod(callerId);
				var castedEngine = engine as IRequestHandlerEngineImpl;
				Check.ArgumentNotNull(castedEngine, "engine as IRequestHandlerEngineImpl");
				if (castedEngine.ServiceLocatorService != null) {
					castedEngine.ServiceLocatorService.GetRegisteredService<IServiceRegistrationService>().RegisterService(
						new UserSessionHandlerService(this, callerId));
				}
				EnginesByCaller.Add(callerId, engine);
			}
			return EnginesByCaller[callerId].Process(request);
		}

		#endregion

		public static IServer Create(delCreateRequestHandlerEngine engine) {
			return Create(engine, ServiceHelper.GetServiceUri());
		}

		public static IServer Create(delCreateRequestHandlerEngine engine, string pServiceURI) {
			if (string.IsNullOrEmpty(pServiceURI)) {
				pServiceURI = ServiceHelper.GetServiceUri();
			}
			return new WcfBasedServer { RequestHandlerEngineCreationMethod = engine, ServiceUri = pServiceURI };
		}

		#region Public Properties

		public Binding NetTcpBinding { get; private set; }
		public String ServiceUri { get; private set; }

		/// <summary>
		/// The creator of the server gave a method which creates a request handler engine. We store the method here.
		/// </summary>
		public delCreateRequestHandlerEngine RequestHandlerEngineCreationMethod { get; private set; }

		/// <summary>
		/// 
		/// </summary>
		public Dictionary<string, IRequestHandlerEngine> EnginesByCaller { get; private set; }

		#endregion

		#region Fields

		private ServiceHost m_host;

		#endregion

		#region Constructors

		/// <summary>
		/// Initializes this class.
		/// </summary>
		private WcfBasedServer() {
			EnginesByCaller = new Dictionary<string, IRequestHandlerEngine>();
		}

		#endregion

		#region Methods

		/// <summary>
		/// Starts the Service Instance.
		/// </summary>
		public void Start() {
			if (NetTcpBinding == null || string.IsNullOrEmpty(ServiceUri)) {
				NetTcpBinding netTcpBinding = ServiceHelper.GetNetTcpBinding();
				NetTcpBinding = netTcpBinding;
			}
			Start(NetTcpBinding, ServiceUri);
		}

		/// <summary>
		/// Stops the Service Instance.
		/// </summary>
		public void Stop() {
			Dispose();
		}

		/// <summary>
		/// Starts the Service Instance.
		/// </summary>
		/// <param name="binding">Predefined <see cref="Binding"/>.</param>
		/// <param name="serviceUri">Service URI as <see langword="string"/>.</param>
		public void Start(Binding binding, string serviceUri) {
			NetTcpBinding = binding;
			ServiceUri = serviceUri;
			if (m_host != null) {
				Dispose();
			}

			Logger.WriteDebug("Start server on:" + serviceUri);

			// Open Service Host
			m_host = new ServiceHost(this);
			m_host.AddServiceEndpoint(typeof(IRemoteRequestHandlerEngine), binding, serviceUri);
			m_host.Open();
		}

		/// <summary>
		/// Disposes the Service Instance.
		/// </summary>
		public void Dispose() {
			if (m_host != null) {
				m_host.Close();
				m_host = null;
			}
		}

		#endregion
	}

	public delegate IRequestHandlerEngine delCreateRequestHandlerEngine(string callerId);
}